package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.RabbitConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.config.sign.RSASignHelper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.order.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import com.atguigu.tingshu.common.Assert;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author sun
 */
@Slf4j
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Resource
    private OrderInfoMapper orderInfoMapper;
    @Resource
    private UserInfoFeignClient userInfoFeignClient;
    @Resource
    private AlbumInfoFeignClient albumInfoFeignClient;
    @Resource
    private TrackInfoFeignClient trackInfoFeignClient;
    @Resource
    private RSASignHelper rsaSignHelper;
    @Resource
    private OrderDerateServiceImpl orderDerateService;
    @Resource
    private OrderDetailServiceImpl orderDetailService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private RedissonClient redissonClient;

    /**
     * 订单确认
     *
     * @param tradeVo 订单确认对象
     * @return 订单VO对象
     */
    @Override
    public OrderInfoVo trade(TradeVo tradeVo) {

        OrderInfoVo orderInfoVo = new OrderInfoVo();

        switch (tradeVo.getItemType()) {
            case SystemConstant.ORDER_ITEM_TYPE_ALBUM -> orderInfoVo = getAlbumOrderInfoVo(tradeVo);
            case SystemConstant.ORDER_ITEM_TYPE_TRACK -> orderInfoVo = getTrackOrderInfoVo(tradeVo);
            case SystemConstant.ORDER_ITEM_TYPE_VIP -> orderInfoVo = getVipOrderInfoVo(tradeVo);
        }
        orderInfoVo.setTimestamp(System.currentTimeMillis());
//        JwtHelper.encode(JSONObject.toJSONString(orderInfoVo),
//                new RsaSigner((RSAPrivateKey)constantsProperties.getPrivateKey())).getEncoded();
        String sign = rsaSignHelper.sign(JSONObject.toJSONString(orderInfoVo));
        orderInfoVo.setSign(sign);
        return orderInfoVo;
    }


    /**
     * 获取VIP订单信息
     *
     * @param tradeVo 交易对象
     * @return 订单VO对象
     */
    private OrderInfoVo getVipOrderInfoVo(TradeVo tradeVo) {
        // vipId --> vip的类型
        Long vipId = tradeVo.getItemId();
        // 获取vip的服务配置
        VipServiceConfig vipServiceConfig = userInfoFeignClient.getVipServiceConfig(vipId);
        Assert.notNull(vipServiceConfig, ResultCodeEnum.VIP_CONFIG_ERROR);
        // 返回对象
        // 折后价
        BigDecimal discountPrice = vipServiceConfig.getDiscountPrice();
        BigDecimal price = vipServiceConfig.getPrice();
        // 减免金额
        BigDecimal deratePrice = price.subtract(discountPrice);
        // 订单减免信息
        ArrayList<OrderDerateVo> orderDerateVos = new ArrayList<>();
        orderDerateVos.add(OrderDerateVo.builder()
                .derateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT)
                .derateAmount(deratePrice)
                .remarks("VIP服务折")
                .build());
        // 订单详细信息
        ArrayList<OrderDetailVo> orderDetailVos = new ArrayList<>();
        orderDetailVos.add(OrderDetailVo.builder()
                .itemId(vipId)
                .itemName(vipServiceConfig.getName())
                .itemUrl(vipServiceConfig.getImageUrl())
                .itemPrice(discountPrice)
                .build());
        return OrderInfoVo.builder()
                .tradeNo(IdWorker.get32UUID())
                .itemType(SystemConstant.ORDER_ITEM_TYPE_VIP)
                .originalAmount(price)
                .derateAmount(deratePrice)
                .orderAmount(discountPrice)
                .orderDerateVoList(orderDerateVos)
                .orderDetailVoList(orderDetailVos)
                .build();
    }

    /**
     * 获取专辑订单信息
     *
     * @param tradeVo 交易对象
     * @return 订单VO对象
     */
    private OrderInfoVo getAlbumOrderInfoVo(TradeVo tradeVo) {
        Long albumId = tradeVo.getItemId();
        AlbumInfo albumInfo = albumInfoFeignClient.getAlbumInfo(albumId);
        Assert.notNull(albumInfo, ResultCodeEnum.ALBUM_NOT_EXIST);
        Assert.notNull(albumInfo.getId(), ResultCodeEnum.ALBUM_NOT_EXIST);
        // 查询用户是否购买了这个专辑
        Assert.isTrue(!userInfoFeignClient.isPaidAlbum(albumId), ResultCodeEnum.ALBUM_HAS_PAID);
        // 返回对象组装
        BigDecimal price = albumInfo.getPrice();
        // 打折
        BigDecimal discount = albumInfo.getDiscount();
        String msg = "专辑普通折扣";
        UserInfo userInfo = userInfoFeignClient.getUserInfoById(albumInfo.getUserId());
        Assert.notNull(userInfo, ResultCodeEnum.ACCOUNT_STOP);
        boolean isVip = userInfo.getIsVip() == 1 && userInfo.getVipExpireTime().after(new Date());
        if (isVip) {
            discount = albumInfo.getVipDiscount();
            msg = "专辑VIP折扣";
        }
        discount = discount.intValue() == -1 ? BigDecimal.ONE :
                discount.multiply(new BigDecimal("0.1"));
        BigDecimal discountPrice = price.multiply(discount);
        BigDecimal deratePrice = price.subtract(discountPrice);
        // 订单减免信息
        ArrayList<OrderDerateVo> orderDerateVos = new ArrayList<>();
        orderDerateVos.add(OrderDerateVo.builder()
                .derateType(isVip ? SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT :
                        SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT)
                .derateAmount(deratePrice)
                .remarks(msg)
                .build());
        // 订单详细信息
        ArrayList<OrderDetailVo> orderDetailVos = new ArrayList<>();
        orderDetailVos.add(OrderDetailVo.builder()
                .itemId(albumId)
                .itemName(albumInfo.getAlbumTitle())
                .itemUrl(albumInfo.getCoverUrl())
                .itemPrice(discountPrice)
                .build());
        return OrderInfoVo.builder()
                .tradeNo(IdWorker.get32UUID())
                .itemType(SystemConstant.ORDER_ITEM_TYPE_ALBUM)
                .originalAmount(price)
                .derateAmount(deratePrice)
                .orderAmount(discountPrice)
                .orderDerateVoList(orderDerateVos)
                .orderDetailVoList(orderDetailVos)
                .build();
    }

    /**
     * 获取声音订单信息
     *
     * @param tradeVo 交易对象
     * @return 订单VO对象
     */
    private OrderInfoVo getTrackOrderInfoVo(TradeVo tradeVo) {
        Long trackId = tradeVo.getItemId();
        Integer trackCount = tradeVo.getTrackCount();
        TrackInfo trackInfo = trackInfoFeignClient.getTrackInfo(trackId);
        Assert.notNull(trackInfo, ResultCodeEnum.TRACK_NOT_EXIST);
        // 要购买的全部声音
        List<TrackInfo> trackInfos = trackInfoFeignClient.getTrackInfoList(trackInfo.getAlbumId(), trackInfo.getOrderNum(), trackCount);
        Assert.notEmpty(trackInfos, ResultCodeEnum.TRACK_NOT_EXIST);
        // 专辑信息
        AlbumInfo albumInfo = albumInfoFeignClient.getAlbumInfo(trackInfo.getAlbumId());
        Assert.notNull(albumInfo, ResultCodeEnum.ALBUM_NOT_EXIST);
        // 订单信息组装
        BigDecimal trackPrice = albumInfo.getPrice();
        BigDecimal totalPrice = trackPrice.multiply(new BigDecimal(trackCount));
        // 订单减免信息---> 声音单个购买无折扣
        ArrayList<OrderDerateVo> orderDerateVos = new ArrayList<>();
        // 订单详细信息
        ArrayList<OrderDetailVo> orderDetailVos = new ArrayList<>();
        trackInfos.forEach(track -> orderDetailVos.add(OrderDetailVo.builder()
                .itemId(track.getId())
                .itemName(track.getTrackTitle())
                .itemUrl(track.getCoverUrl())
                .itemPrice(trackPrice)
                .build()));

        return OrderInfoVo.builder()
                .tradeNo(IdWorker.get32UUID())
                .itemType(SystemConstant.ORDER_ITEM_TYPE_ALBUM)
                .originalAmount(totalPrice)
                .derateAmount(BigDecimal.ZERO)
                .orderAmount(totalPrice)
                .orderDerateVoList(orderDerateVos)
                .orderDetailVoList(orderDetailVos)
                .build();
    }


    /**
     * 提交订单/新增订单
     *
     * @param orderInfoVo 订单VO对象
     * @return 订单ID
     */
    @Override
    @Transactional
    public Map<String, Object> submitOrder(OrderInfoVo orderInfoVo) {
        // 校验签名
        String sign = orderInfoVo.getSign();
        orderInfoVo.setSign(null);
        String payWay = orderInfoVo.getPayWay();
        orderInfoVo.setPayWay(null);

        // TODO 这里购买会员验签错误
        boolean verify = rsaSignHelper.verify(JSONObject.toJSONString(orderInfoVo), sign);
        Assert.isTrue(verify, ResultCodeEnum.SIGN_VERIFY_ERROR);
        // 校验时间
        if (System.currentTimeMillis() - orderInfoVo.getTimestamp() > 1000 * 60 * 5) {
            throw new RuntimeException("页面停留时间过长");
        }

        // 防止重复提交 ；防止未支付的订单重复提交 ；防止提交订单中含有未支付订单中的内容；

        Long userId = AuthContextHolder.getUserId();
        String itemType = orderInfoVo.getItemType();
        String key = "lock:submitOrder:" + userId + ":" + itemType;
        stringRedisTemplate.opsForValue().setIfAbsent(key, "", 15, TimeUnit.MINUTES);
        try {
            // 组合订单对象，入库
            OrderInfo orderInfo = new OrderInfo();
            BeanUtils.copyProperties(orderInfoVo, orderInfo);
            orderInfo.setPayWay(payWay);
            orderInfo.setUserId(userId);
            switch (itemType) {
                case SystemConstant.ORDER_ITEM_TYPE_ALBUM -> orderInfo.setOrderTitle("专辑订单");
                case SystemConstant.ORDER_ITEM_TYPE_TRACK -> orderInfo.setOrderTitle("声音订单");
                case SystemConstant.ORDER_ITEM_TYPE_VIP -> orderInfo.setOrderTitle("会员订单");
            }
            orderInfo.setOrderNo(orderInfoVo.getTradeNo());
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
            // 插入订单
            orderInfoMapper.insert(orderInfo);

            List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();

            Long orderId = orderInfo.getId();
            if (!CollectionUtils.isEmpty(orderDerateVoList)) {
                List<OrderDerate> collect = orderDerateVoList.stream().map(
                        orderDerateVo -> OrderDerate.builder()
                                .orderId(orderId)
                                .derateType(orderDerateVo.getDerateType())
                                .derateAmount(orderDerateVo.getDerateAmount())
                                .remarks(orderDerateVo.getRemarks())
                                .build()
                ).collect(Collectors.toList());
                // 批量插入
                orderDerateService.saveBatch(collect);
            }
            List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
            if (!CollectionUtils.isEmpty(orderDetailVoList)) {
                List<OrderDetail> collect = orderDetailVoList.stream().map(
                        orderDetailVo -> OrderDetail.builder()
                                .orderId(orderId)
                                .itemId(orderDetailVo.getItemId())
                                .itemName(orderDetailVo.getItemName())
                                .itemUrl(orderDetailVo.getItemUrl())
                                .itemPrice(orderDetailVo.getItemPrice())
                                .build()).collect(Collectors.toList());
                // 批量插入
                orderDetailService.saveBatch(collect);
            }

            HashMap<String, Object> map = new HashMap<>();

            map.put("orderNo", orderInfo.getOrderNo());

            //TODO 发送消息延迟删除 未支付订单
            rabbitTemplate.convertAndSend(RabbitConstant.ORDER_EXCHANGE,RabbitConstant.ORDER_DELAY_KEY, OrderMsg.builder()
                    .orderId(orderId)
                    .userId(userId)
                    .itemType(itemType)
                    .build());

            return map;
        } catch (Exception e) {
            // 删除锁
            stringRedisTemplate.opsForValue().getAndDelete(key);
            throw new RuntimeException(e);
        }
    }

    /**
     * 取消订单
     *
     * @param msg 订单删除信息
     */
    @Override
    public void cancelOrder(OrderMsg msg) {
        // TODO 这里不需要用orderMsg来传递消息
        // 加锁防止多线程操作订单
        String key = "lock:order:" + msg.getOrderNo();
        RLock lock = redissonClient.getLock(key);
        if (lock.tryLock()) {
            try {
                OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getOrderNo, msg.getOrderNo())
                        .eq(OrderInfo::getOrderStatus, SystemConstant.ORDER_STATUS_UNPAID)
                );
                if (orderInfo == null) {
                    return;
                }
                orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
                orderInfoMapper.updateById(orderInfo);
                stringRedisTemplate.delete("lock:submitOrder:" + msg.getUserId() + ":" + msg.getItemType());
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * 订单支付成功
     *
     * @param orderNo 订单编号
     */
    @Override
    public void successOrder(String orderNo) {
        // 加锁防止多线程操作订单
        String key = "lock:order:" + orderNo;
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>()
                    .eq(OrderInfo::getOrderNo, orderNo)
            );
            if (orderInfo == null) {
                // 直接返回, 订单不存在---不要抛出异常异常为认为消息消费失败
                return;
            }
            if(orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_PAID)){
                return;
            }
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);
            stringRedisTemplate.delete("lock:submitOrder:" + orderInfo.getUserId() + ":" + orderInfo.getItemType());
            // TODO 发送消息通知用户支付成功
        }finally {
            lock.unlock();
        }


    }
}